#nullable enable

using System.Text.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using PHMEE.CommonUtils;
using PHMEE.EnergyEfficiency;
using PHMEE.TDengineInfrastructure;

namespace PHMEE.Admin.Services.DataSync;

public class DataSyncShoreService : BackgroundService
{
    private readonly ILogger<DataSyncShoreService> _logger;
    private readonly IConfiguration _configuration;
    private readonly ITimeSeriesDataLogRepository _timeSeriesDataLogRepository;
    private readonly ITimeSeriesTombstonesRepository _timeSeriesTombstonesRepository;
    private readonly ILogAbstractRepository _logAbstractRepository;
    private readonly IBunkerReportRepository _bunkerReportRepository;
    private readonly IPHMEETimeProvider _phmeeTimeProvider;
    private Task? _executingTask;
    private readonly SemaphoreSlim _syncLock = new(1, 1);

    public DataSyncShoreService(ILogger<DataSyncShoreService> logger,
        IConfiguration configuration,
        ITimeSeriesDataLogRepository timeSeriesDataLogRepository,
        ITimeSeriesTombstonesRepository timeSeriesTombstonesRepository,
        ILogAbstractRepository logAbstractRepository,
        IBunkerReportRepository bunkerReportRepository,
        IPHMEETimeProvider phmeeTimeProvider)
    {
        _logger = logger;
        _configuration = configuration;
        _timeSeriesDataLogRepository = timeSeriesDataLogRepository;
        _timeSeriesTombstonesRepository = timeSeriesTombstonesRepository;
        _logAbstractRepository = logAbstractRepository;
        _bunkerReportRepository = bunkerReportRepository;
        _phmeeTimeProvider = phmeeTimeProvider;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        try
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                var utcNow = _phmeeTimeProvider.GetUtcNow();
                
                _logger.LogInformation("数据同步任务运行: {time}", utcNow.ToString(":yyyy-MM-ddTHH:mm:ss.fffZ"));

                try
                {
                    await _syncLock.WaitAsync(stoppingToken);
                    _executingTask = DoWorkAsync(stoppingToken);
                    await _executingTask;
                }
                catch (OperationCanceledException)
                {
                    _logger.LogInformation("数据同步任务被取消");
                    break;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "数据同步任务出现异常");
                }
                finally
                {
                    _syncLock.Release();
                }

                // 等待 1 分钟后再次执行
                await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
            }
        }
        catch (OperationCanceledException)
        {
            _logger.LogInformation("数据同步任务被取消");
        }
    }

    private async Task DoWorkAsync(CancellationToken stoppingToken)
    {
        var allUnSyncedLogs = await _timeSeriesDataLogRepository.GetListUnSyncedOperationTimeAscAsync(stoppingToken);
        if (allUnSyncedLogs.Count == 0)
        {
            _logger.LogInformation("没有待同步数据");
            return;
        }
        
        var allLogIds = allUnSyncedLogs.Select(x => x.Id).ToList();
        _logger.LogInformation("CBM 开始同步数据，待同步共 {count} 条数据, id: {allLogIds}", allUnSyncedLogs.Count, allLogIds);
        
        foreach (var log in allUnSyncedLogs)
        {
            Guid? syncedLogId;

            switch (log.OperationType)
            {
                // 同步 Insert 的数据
                case OperationType.Insert:
                    syncedLogId = await HandleInsert(log);
                    break;
                // 同步 Delete 的数据
                case OperationType.Delete:
                    syncedLogId = await HandleDelete(log);
                    break;
                default:
                    // 记录日志
                    _logger.LogInformation("未识别的 OperationType: {OperationType}, VesselId: {vesselId}, log.Id: {Id}",
                        log.OperationType.ToString(), log.VesselId, log.Id);
                    // 跳过这条日志，继续处理下一条
                    continue;
            }

            // 更新同步状态
            if (syncedLogId is not null)
            {
                log.IsSynced = true;
                await _timeSeriesDataLogRepository.UpdateAsync(log, cancellationToken: stoppingToken);
                _logger.LogInformation("成功同步一条日志: {logId}", syncedLogId);
            }
        }
    }

    private async Task<Guid?> HandleInsert(TimeSeriesDataLog log)
    {
        switch (log.SyncDataType)
        {
            case SyncDataType.LogAbstract:
                return await HandleLAInsert(log);
            case SyncDataType.BunkerReport:
                return await HandleBRInsert(log);
            default:
                // 记录日志
                _logger.LogInformation("未识别的 SyncDataType: {SyncDataType}, vesselId: {vesselId}, log.Id: {Id}",
                    log.SyncDataType.ToString(), log.VesselId, log.Id);
                return null;
        }
    }

    private async Task<Guid?> HandleLAInsert(TimeSeriesDataLog log)
    {
        // Insert 时，JsonDatum 不能为空
        if (string.IsNullOrWhiteSpace(log.Datum))
        {
            // 记录日志
            _logger.LogInformation("VesselId: {vesselId} JsonDatum 为空，不插入新的 LogAbstract。", log.Id);
            return null;
        }

        LogAbstract? newLogAbstract =
            JsonSerializer.Deserialize<LogAbstract>(log.Datum, DataSyncJsonSerializerExtensions.Options);
        if (newLogAbstract is null)
        {
            // 记录日志
            _logger.LogInformation(
                "VesselId: {vesselId}, logId: {id} - JsonDatum 无法反序列化为 LogAbstract，不插入新的 LogAbstract。",
                log.VesselId, log.Id);
            return null;
        }

        var existingLogAbstract =
            await _logAbstractRepository.GetOrDefaultAsync(log.VesselId,
                newLogAbstract.Ts);

        // 如果已经存在相同时间戳的 LogAbstract，则比较他们的 LastModifyTime，如果新的 LogAbstract 的 LastModifyTime 早于已存在的，则不插入
        if (existingLogAbstract is not null
            && newLogAbstract.LastModifiedTime < existingLogAbstract.LastModifiedTime)
        {
            // 记录日志
            _logger.LogInformation(
                "VesselId: {vesselId} 新的 LogAbstract 的 LastModifiedTime 早于已存在的，不插入新的 LogAbstract。 log.Id: {id}, LA.ts: {ts}",
                log.VesselId, log.Id, newLogAbstract.Ts);

            // 这种情况下， 应该认为同步成功并且返回 Id，因为数据将来无论如何都不会被插入，不应让船端继续反复同步数据
            return log.Id;
        }

        // 检查墓碑表，如果墓碑表中的记录的 DeletedAt 时间早于当前的 OperationTime，则忽略这条墓碑记录，插入新的 LogAbstract
        // 如果墓碑表中的记录的 DeletedAt 时间晚于或等于当前的 OperationTime，则不插入新的 LogAbstract，因为这意味着数据已被删除且不应该被重新插入
        var tombstone = await GetTombstones(log);

        if (tombstone is not null && tombstone.DeletedAt >= log.OperationTime)
        {
            // 记录日志
            _logger.LogInformation(
                "VesselId: {vesselId} 墓碑表中的记录的 DeletedAt 时间晚于或等于当前的 OperationTime，不插入新的 LogAbstract。log.Id: {id}, LA.ts: {ts}",
                log.VesselId, log.Id, newLogAbstract.Ts);

            // 这种情况下， 应该认为同步成功并且返回 Id，因为数据将来无论如何都不会被插入，不应让船端继续反复同步数据
            return log.Id;
        }

        try
        {
            await _logAbstractRepository.InsertAsync(
                newLogAbstract,
                log.VesselId);
        }
        catch (TDengineRepositoryException ex)
        {
            // 记录日志
            _logger.LogError(ex, "VesselId: {vesselId} 插入新的 LogAbstract 失败。log.Id: {id}, LA.ts: {ts}",
                log.VesselId, log.Id, newLogAbstract.Ts);
            return null;
        }

        // 记录日志
        _logger.LogInformation("VesselId: {vesselId} 插入新的 LogAbstract 成功。log.Id: {id}, LA.ts: {ts}",
            log.VesselId, log.Id, newLogAbstract.Ts);

        return log.Id;
    }

    private async Task<Guid?> HandleBRInsert(TimeSeriesDataLog log)
    {
        if (string.IsNullOrWhiteSpace(log.Datum))
        {
            // 记录日志
            _logger.LogInformation("VesselId: {vesselId} JsonDatum 为空，不插入新的 BunkerReport。 log.Id: {id}, log.Ts: {ts}",
                log.VesselId, log.Id, log.Ts);
            return null;
        }

        BunkerReport? newBunkerReport =
            JsonSerializer.Deserialize<BunkerReport>(log.Datum, DataSyncJsonSerializerExtensions.Options);
        if (newBunkerReport is null)
        {
            // 记录日志
            _logger.LogInformation(
                "VesselId: {vesselId} JsonDatum 无法反序列化为 BunkerReport，不插入新的 BunkerReport。, log.Id: {id}, log.Ts: {ts}",
                log.VesselId, log.Id, log.Ts);
            return null;
        }

        var existingBunkerReport =
            await _bunkerReportRepository.GetAsync(log.VesselId,
                newBunkerReport.Ts);
        // 如果已经存在相同时间戳的 BunkerReport，则比较他们的 LastModifyTime，如果新的 BunkerReport 的 LastModifyTime 早于已存在的，则不插入
        if (existingBunkerReport is not null
            && newBunkerReport.LastModifiedTime < existingBunkerReport.LastModifiedTime)
        {
            // 记录日志
            _logger.LogInformation(
                "VesselId: {vesselId} 墓碑表中的记录的 DeletedAt 时间晚于或等于当前的 OperationTime，不插入新的 BunkerReport。 log.Id: {id}, BR.ts: {ts}",
                log.VesselId, log.Id, newBunkerReport.Ts);
            return log.Id;
        }

        // 检查墓碑表，如果墓碑表中的记录的 DeletedAt 时间早于当前的 OperationTime，则忽略这条墓碑记录，插入新的 LogAbstract
        // 如果墓碑表中的记录的 DeletedAt 时间晚于或等于当前的 OperationTime，则不插入新的 LogAbstract，因为这意味着数据已被删除且不应该被重新插入
        var tombstone = await GetTombstones(log);

        if (tombstone is not null && tombstone.DeletedAt >= log.OperationTime)
        {
            // 记录日志
            _logger.LogInformation(
                "VesselId: {vesselId} 墓碑表中的记录的 DeletedAt 时间晚于或等于当前的 OperationTime，不插入新的 BunkerReport。 log.Id: {id}, BR.ts: {ts}",
                log.VesselId, log.Id, newBunkerReport.Ts);
            return log.Id;
        }

        try
        {
            await _bunkerReportRepository.InsertAsync(
                newBunkerReport,
                log.VesselId);
        }
        catch (TDengineRepositoryException ex)
        {
            // 记录日志
            _logger.LogError(ex, "VesselId: {vesselId} 插入新的 BunkerReport 失败。log.Id: {id}, BR.ts: {ts}",
                log.VesselId, log.Id, newBunkerReport.Ts);
            return null;
        }

        // 记录日志
        _logger.LogInformation("VesselId: {vesselId} 插入新的 BunkerReport 成功。log.Id: {id}, BR.ts: {ts}",
            log.VesselId, log.Id, newBunkerReport.Ts);
        return log.Id;
    }

    private async Task<Guid?> HandleDelete(TimeSeriesDataLog log)
    {
        // Delete 时需要先判断现有的 LogAbstract 或 BunkerReport 的 LastModifyTime 是否晚于 OperationTime，如果晚于，则不删除，说明删除的 message 是延迟到达的
        switch (log.SyncDataType)
        {
            case SyncDataType.LogAbstract:
                return await HandleLADelete(log);
            case SyncDataType.BunkerReport:
                return await HandleBRDelete(log);
            default:
                // 记录日志
                _logger.LogInformation("未识别的 SyncDataType: {SyncDataType}, vesselId: {vesselId}, log.Id: {id}",
                    log.SyncDataType.ToString(), log.VesselId, log.Id);
                return null;
        }
    }

    private async Task<Guid?> HandleLADelete(TimeSeriesDataLog log)
    {
        var existingLogAbstract =
            await _logAbstractRepository.GetOrDefaultAsync(log.VesselId, log.Ts);

        // 如果已经存在相同时间戳的 LogAbstract，则比较他们的 LastModifyTime
        // 如果已存在的 LogAbstract 的 LastModifyTime 晚于 OperationTime，则不删除，说明删除的 message 是延迟到达的
        if (existingLogAbstract is not null &&
            existingLogAbstract.LastModifiedTime > log.OperationTime)
        {
            // 记录日志
            _logger.LogInformation(
                "VesselId: {vesselId} 已存在的 LogAbstract 的 LastModifiedTime 晚于 OperationTime，不删除 LogAbstract。 log.Id: {Id}, LA.ts: {ts}",
                log.VesselId, log.Id, log.Ts);
            return log.Id;
        }

        try
        {
            await _logAbstractRepository.DeleteAsync(
                log.VesselId,
                log.Ts);
        }
        catch (TDengineRepositoryException ex)
        {
            // 记录日志
            _logger.LogError(ex, "VesselId: {vesselId} 删除 LogAbstract 失败。 log.Id: {id}, LA.ts: {ts}",
                log.VesselId, log.Id, log.Ts);
            return null;
        }

        // 往墓碑表中插入记录
        await _timeSeriesTombstonesRepository.InsertAsync(new TimeSeriesTombstones(
            log.Ts,
            log.VesselId, SyncDataType.LogAbstract, log.OperationTime));

        // 记录日志
        _logger.LogInformation("VesselId: {vesselId} 删除 LogAbstract 成功。log.Id: {id}, LA.ts: {ts}",
            log.VesselId, log.Id, log.Ts);
        return log.Id;
    }

    private async Task<Guid?> HandleBRDelete(TimeSeriesDataLog log)
    {
        var existingBunkerReport =
            await _bunkerReportRepository.GetAsync(log.VesselId, log.Ts);

        // 如果已经存在相同时间戳的 BunkerReport，则比较他们的 LastModifyTime
        // 如果已存在的 BunkerReport 的 LastModifyTime 晚于 OperationTime，则不删除，说明删除的 message 是延迟到达的

        if (existingBunkerReport is not null &&
            existingBunkerReport.LastModifiedTime > log.OperationTime)
        {
            // 记录日志
            _logger.LogInformation(
                "VesselId: {vesselId} 已存在的 BunkerReport 的 LastModifiedTime 晚于 OperationTime，不删除 BunkerReport。 log.Id: {id}, BR.ts: {ts}",
                log.VesselId, log.Id, log.Ts);
            return log.Id;
        }

        try
        {
            await _bunkerReportRepository.DeleteAsync(log.VesselId, log.Ts);
        }
        catch (TDengineRepositoryException ex)
        {
            // 记录日志
            _logger.LogError(ex, "VesselId: {vesselId} 删除 BunkerReport 失败。 log.Id: {id}, BR.ts: {ts}",
                log.VesselId, log.Id, log.Ts);
            return null;
        }


        // 往墓碑表中插入记录
        await _timeSeriesTombstonesRepository.InsertAsync(new TimeSeriesTombstones(
            log.Ts,
            log.VesselId, SyncDataType.BunkerReport, log.OperationTime));

        // 记录日志
        _logger.LogInformation("VesselId: {vesselId} 删除 BunkerReport 成功。log.Id: {id}, BR.ts: {ts}", 
            log.VesselId, log.Id, log.Ts);
        return log.Id;
    }

    private async Task<TimeSeriesTombstones?> GetTombstones(TimeSeriesDataLog log)
    {
        var tombstone = await _timeSeriesTombstonesRepository.GetOrDefaultAsync(log.VesselId, log.Ts,
            log.SyncDataType);
        return tombstone;
    }

    public override async Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("数据同步任务开始运行");
        await base.StartAsync(cancellationToken);
    }

    public override async Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("数据同步任务正在停止...");
        
        // 等待当前任务完成
        if (_executingTask != null)
        {
            try
            {
                await _syncLock.WaitAsync(cancellationToken);
                if (_executingTask != null)
                {
                    await _executingTask;
                }
            }
            finally
            {
                _syncLock.Release();
            }
        }

        _logger.LogInformation("数据同步任务已停止");
        await base.StopAsync(cancellationToken);
    }
}